import requests
from bs4 import BeautifulSoup
import os
import time
from urllib.parse import urljoin, urlparse
import re


class ImageCrawler:
    """
    图片爬虫类，用于从多个网页爬取图片并保存到本地
    """

    def __init__(self, save_folder="downloaded_images"):
        """
        初始化爬虫

        参数:
        save_folder: 图片保存的文件夹路径
        """
        self.save_folder = save_folder
        # 设置请求头，模拟浏览器访问，避免被网站屏蔽
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        }
        # 创建会话对象，可以保持cookie和连接，提高效率
        self.session = requests.Session()
        self.session.headers.update(self.headers)

        # 创建保存文件夹，如果不存在则创建
        if not os.path.exists(self.save_folder):
            os.makedirs(self.save_folder)
            print(f"✅ 创建保存文件夹: {self.save_folder}")

    def download_image(self, img_url, filename=None):
        """
        下载单张图片到本地

        参数:
        img_url: 图片的完整URL地址
        filename: 自定义文件名，如果为None则自动生成

        返回:
        (成功状态, 文件名或错误信息)
        """
        try:
            # 如果没有提供文件名，从URL中提取文件名
            if not filename:
                parsed_url = urlparse(img_url)
                filename = os.path.basename(parsed_url.path)
                # 如果提取的文件名无效，使用时间戳生成唯一文件名
                if not filename or '.' not in filename:
                    filename = f"image_{int(time.time())}.jpg"

            # 处理文件名，确保在文件夹中唯一，避免覆盖
            base_name, ext = os.path.splitext(filename)
            counter = 1
            final_filename = filename
            save_path = os.path.join(self.save_folder, final_filename)

            # 如果文件名已存在，添加数字后缀
            while os.path.exists(save_path):
                final_filename = f"{base_name}_{counter}{ext}"
                save_path = os.path.join(self.save_folder, final_filename)
                counter += 1

            # 发送请求下载图片
            print(f"⬇️  正在下载: {img_url}")
            response = self.session.get(img_url, timeout=10)
            response.raise_for_status()  # 检查请求是否成功

            # 将图片内容写入文件
            with open(save_path, 'wb') as f:
                f.write(response.content)

            return True, final_filename

        except Exception as e:
            # 下载失败，返回错误信息
            return False, str(e)

    def crawl_page(self, url, max_images=50):
        """
        爬取单个页面的所有图片

        参数:
        url: 要爬取的网页URL
        max_images: 单页最大下载图片数量

        返回:
        成功下载的文件名列表
        """
        try:
            print(f"🔍 正在解析页面: {url}")
            # 发送HTTP请求获取网页内容
            response = self.session.get(url, timeout=10)
            response.raise_for_status()  # 如果请求失败会抛出异常

            # 使用BeautifulSoup解析HTML内容
            soup = BeautifulSoup(response.text, 'html.parser')
            # 查找页面中所有的img标签
            img_tags = soup.find_all('img')
            print(f"📷 在页面中找到 {len(img_tags)} 个图片标签")

            downloaded_count = 0
            successful_downloads = []  # 记录成功下载的文件名

            # 遍历所有图片标签
            for img in img_tags:
                # 如果达到最大下载数量，停止处理
                if downloaded_count >= max_images:
                    break

                # 获取图片URL，优先考虑src属性，其次是常见的懒加载属性
                img_url = img.get('src') or img.get('data-src') or img.get('data-original')
                if not img_url:
                    continue  # 如果没有找到图片URL，跳过这个标签

                # 处理相对URL，将其转换为绝对URL
                # 例如: '/images/pic.jpg' -> 'https://example.com/images/pic.jpg'
                img_url = urljoin(url, img_url)

                # 检查URL是否为图片格式，过滤掉非图片链接
                if not self.is_image_url(img_url):
                    continue

                # 下载图片
                success, result = self.download_image(img_url)
                if success:
                    downloaded_count += 1
                    successful_downloads.append(result)
                    print(f"✅ 成功下载第 {downloaded_count} 张图片: {result}")
                else:
                    print(f"❌ 下载失败: {result}")

                # 添加延迟，避免请求过于频繁被封IP
                time.sleep(0.3)

            print(f"📊 页面爬取完成，成功下载 {downloaded_count} 张图片")
            return successful_downloads

        except Exception as e:
            print(f"💥 页面爬取失败: {str(e)}")
            return []

    def crawl_multiple_pages(self, urls, max_images_per_page=20):
        """
        批量爬取多个页面的图片

        参数:
        urls: 要爬取的URL列表
        max_images_per_page: 每个页面最大下载图片数量

        返回:
        所有成功下载的文件名列表
        """
        all_downloads = []  # 存储所有下载成功的文件名

        # 遍历URL列表，enumerate用于同时获取索引和URL
        for i, url in enumerate(urls, 1):
            print(f"\n{'=' * 50}")
            print(f"🌐 正在处理第 {i}/{len(urls)} 个页面: {url}")
            print(f"{'=' * 50}")

            # 爬取单个页面
            downloads = self.crawl_page(url, max_images_per_page)
            all_downloads.extend(downloads)  # 将结果添加到总列表中

            # 如果不是最后一个页面，添加页面间的延迟
            if i < len(urls):
                print("⏳ 页面间延迟中...")
                time.sleep(1)  # 页面间延迟，避免请求过于密集

        # 输出最终统计信息
        print(f"\n🎉 所有页面爬取完成！")
        print(f"📈 总计下载 {len(all_downloads)} 张图片")
        print(f"💾 图片保存在: {os.path.abspath(self.save_folder)}")

        return all_downloads

    @staticmethod
    def is_image_url(url):
        """
        静态方法：检查URL是否为图片链接

        参数:
        url: 要检查的URL

        返回:
        布尔值，True表示是图片URL
        """
        # 定义常见的图片文件扩展名模式
        image_patterns = [
            r'\.jpg$',  # 匹配以.jpg结尾
            r'\.jpeg$',  # 匹配以.jpeg结尾
            r'\.png$',  # 匹配以.png结尾
            r'\.gif$',  # 匹配以.gif结尾
            r'\.bmp$',  # 匹配以.bmp结尾
            r'\.webp$',  # 匹配以.webp结尾
            r'\.jpg\?',  # 匹配.jpg后带参数的情况
            r'\.jpeg\?',  # 匹配.jpeg后带参数的情况
            r'\.png\?',  # 匹配.png后带参数的情况
            r'\.gif\?',  # 匹配.gif后带参数的情况
            r'\.bmp\?',  # 匹配.bmp后带参数的情况
            r'\.webp\?'  # 匹配.webp后带参数的情况
        ]
        # 检查URL是否匹配任何图片模式，re.I表示忽略大小写
        return any(re.search(pattern, url, re.I) for pattern in image_patterns)


# 使用示例和演示
if __name__ == "__main__":
    """
    主程序入口，演示如何使用ImageCrawler类
    """

    # 创建爬虫实例，指定图片保存文件夹
    crawler = ImageCrawler("my_downloaded_images")

    # 定义要爬取的页面URL列表
    # 注意：请将这些示例URL替换为实际要爬取的网站
    urls_to_crawl = [
        "https://example.com/page1",  # 替换为实际URL
        "https://example.com/page2",  # 替换为实际URL
        "https://example.com/page3"  # 替换为实际URL
    ]

    # 开始批量爬取
    # 参数1: URL列表
    # 参数2: 每个页面最多下载15张图片
    downloaded_files = crawler.crawl_multiple_pages(urls_to_crawl, max_images_per_page=15)

    # 打印下载结果摘要
    if downloaded_files:
        print(f"\n📋 下载文件列表:")
        for file in downloaded_files:
            print(f"   📄 {file}")
    else:
        print("\n⚠️  没有成功下载任何图片")